﻿using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace AliCMS.UserModule
{
    public class WxPayPartner
	{
        public static string BuildAuthorization(string method, string url, long timestamp, string nonceStr, string body)
        {
            string prefix = "WECHATPAY2-SHA256-RSA2048";//固定字符串
            string signature = BuildSign(method, url, timestamp, nonceStr, body);
            string result = string.Format("{0} mchid=\"{1}\",serial_no=\"{2}\",nonce_str=\"{3}\",timestamp=\"{4}\",signature=\"{5}\"", prefix, MERCHANT_ID, MERCHANT_SERIAL_NO, nonceStr, timestamp, signature);

            return result;
        }

        /**
         * 构造签名串
         *
         * @param method    {@link RequestMethod} GET,POST,PUT等
         * @param url       请求接口 /v3/certificates
         * @param timestamp 获取发起请求时的系统当前时间戳
         * @param nonceStr  随机字符串
         * @param body      请求报文主体
         * @return 待签名字符串
         * 
         * （1）经测试【方法2】和【方法5】的结果一样
         */
        public static string BuildSign(string method, string url, long timestamp, string nonceStr, string body)
        {
            string text = string.Join("\n", method, new Uri(url).PathAndQuery, timestamp, nonceStr, body, null);

            X509Certificate2 certificate = new X509Certificate2(MERCHANT_PRIVATE_KEY_FILE_PATH, MERCHANT_PRIVATE_KEY_PASSWORD, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
            var rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
            var rsaClear = new RSACryptoServiceProvider();
            var paras = rsa.ExportParameters(true);
            rsaClear.ImportParameters(paras);

            return Convert.ToBase64String(rsaClear.SignData(Encoding.UTF8.GetBytes(text), "SHA256"));
        }
        public static long GetTimestamp()
        {
            return ToUnixTimeSeconds();
        }
        public static string GetNonceStr()
        {
            return Guid.NewGuid().ToString().Replace("-", "");
        }

        private static long ToUnixTimeSeconds()
        {
            long num = DateTimeOffset.Now.UtcDateTime.Ticks / 10000000L;
            return num - 62135596800L;
        }

        #region AEAD_AES_256_GCM解密
        /// <summary>
        /// 使用BouncyCastle进行AEAD_AES_256_GCM 解密
        /// </summary>
        /// <param name="key">key:32位字符</param>
        /// <param name="nonce">随机串12位</param>
        /// <param name="cipherData">密文(Base64字符)</param>
        /// <param name="associatedData">附加数据可能null</param>
        /// <returns></returns>
        public static string AesGcmDecryptByBouncyCastle(string key, string nonce, string cipherData, string associatedData)
        {
            var associatedBytes = associatedData == null ? null : Encoding.UTF8.GetBytes(associatedData);

            var gcmBlockCipher = new GcmBlockCipher(new AesEngine());
            var parameters = new AeadParameters(
                new KeyParameter(Encoding.UTF8.GetBytes(key)),
                128,  //128 = 16 * 8 => (tag size * 8)
                Encoding.UTF8.GetBytes(nonce),
                associatedBytes);
            gcmBlockCipher.Init(false, parameters);

            var data = Convert.FromBase64String(cipherData);
            var plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];

            var length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
            gcmBlockCipher.DoFinal(plaintext, length);
            return Encoding.UTF8.GetString(plaintext);
        }

        /// <summary>
        /// 使用BouncyCastle进行AEAD_AES_256_GCM 加密
        /// </summary>
        /// <param name="key">key32位字符</param>
        /// <param name="nonce">随机串12位</param>
        /// <param name="plainData">明文</param>
        /// <param name="associatedData">附加数据可能null</param>
        /// <returns></returns>
        public static string AesGcmEncryptByBouncyCastle(string key, string nonce, string plainData, string associatedData)
        {
            var associatedBytes = associatedData == null ? null : Encoding.UTF8.GetBytes(associatedData);

            var gcmBlockCipher = new GcmBlockCipher(new AesEngine());
            var parameters = new AeadParameters(
                new KeyParameter(Encoding.UTF8.GetBytes(key)),
                128, //128 = 16 * 8 => (tag size * 8)
                Encoding.UTF8.GetBytes(nonce),
                associatedBytes);
            gcmBlockCipher.Init(true, parameters);

            var data = Encoding.UTF8.GetBytes(plainData);
            var cipherData = new byte[gcmBlockCipher.GetOutputSize(data.Length)];

            var length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, cipherData, 0);
            gcmBlockCipher.DoFinal(cipherData, length);
            return Convert.ToBase64String(cipherData);
        }
        #endregion

        #region v2接口，退款信息解密
        public static string DecodeReqInfo(string reqInfo)
        {
            //MD5加密
            var md5 = MD5.Create();
            var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(MERCHANT_API_KEY));
            var sb = new StringBuilder();
            foreach (byte b in bs)
            {
                sb.Append(b.ToString("x2"));
            }
            //所有字符转为大写
            string key = sb.ToString().ToLower();

            return DecodeAES256ECB(reqInfo, key);
        }
        private static string DecodeAES256ECB(string s, string key)
        {
            string r = null;
            try
            {
                byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
                byte[] toEncryptArray = Convert.FromBase64String(s);
                RijndaelManaged rDel = new RijndaelManaged();
                rDel.Key = keyArray;
                rDel.Mode = CipherMode.ECB;
                rDel.Padding = PaddingMode.PKCS7;
                ICryptoTransform cTransform = rDel.CreateDecryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                r = UTF8Encoding.UTF8.GetString(resultArray);
            }
            catch { }
            return r;
        }
        #endregion

        #region Properties
        #endregion

        #region Fields
        #endregion

        #region 常量
        public static string WXPP_APPID = "wx1223dff3d229d969";//支付服务商 - 对应公众号AppID
        public static string WXPP_APPSECRET = "09f604c9940609d5d42b9db4dbfe9ce6";//支付服务商 - 对应公众号AppSecret
        public static string MERCHANT_ID = "1504626521";//支付服务商 - 商户号
        public static string MERCHANT_SERIAL_NO = "2399AD27131BF192577DDC2A055264AD1CD530F6";//支付服务商 - 证书序列号
        public static string MERCHANT_PRIVATE_KEY_FILE_PATH = @"D:\ilghar\ssl\apiclient_cert.p12";//支付服务商 - 证书文件路径
		public static string MERCHANT_PRIVATE_KEY_PASSWORD = "1504626521";//支付服务商 - 证书密码
        public static string MERCHANT_API_KEY = "8B4738CE62E94ED6900892427EEEC99B";//支付服务商 - API秘钥
        public static string MERCHANT_APIV3_KEY = "8B4738CE62E94ED6900892427EEEC99B";//支付服务商 - APIv3秘钥
        #endregion
    }
}
